const xml2js = require('xml2js');
const axios = require('axios');
const fs = require('fs');
const https = require('https');
const path = require('path');

const security = require('../security');
const { generateRandomUUID } = require('../../module/tool/common');

const wxConfig = require('../../../config/wxapi.json');

const WxApiError = require('../../module/error/wxapierror');
const moment = require('moment');

// 回调地址
function getPaymentNotifyHost () {
    return process.env.NODE_ENV === 'trial' ? wxConfig.payment.trialNotifyHost : wxConfig.payment.releaseNotifyHost;
}

// xml解析工具
function getXmlTool () {
    let xmlBuilder = new xml2js.Builder({
        cdata: true,
        rootName: 'xml',
        headless: true
    });
    let xmlParser = new xml2js.Parser({
        explicitArray: false,
        cdata: true,
        rootName: 'xml',
        headless: true
    });
    return [xmlBuilder, xmlParser];
}

/**
 * 统一下单接口
 * @param attach        额外参数
 * @param outTradeOn    外系统订单号
 * @param totalFee      总价格
 * @param openid        用户openid
 * @param description   下单描述
 * @param notifyUrl     支付回调地址
 * @returns prepayId    预支付参数
 */
async function makeMehBill (attach, outTradeOn, totalFee, openid, description, notifyUrl) {
    let [xmlBuilder, xmlParser] = getXmlTool();
    const { data } = await axios.get('https://ip.3322.org');
    let payData = {
        appid: wxConfig.secret.appId,
        mch_id: wxConfig.payment.mchId,
        device_info: 'dlmn_backend',
        nonce_str: generateRandomUUID(),
        sign_type: 'HMAC-SHA256',
        body: description,
        attach: attach,
        out_trade_no: outTradeOn,
        total_fee: totalFee,
        spbill_create_ip: data,
        notify_url: notifyUrl,
        trade_type: 'JSAPI',
        openid: openid,
        time_start: moment().format('yyyyMMDDHHmmss'),
        time_expire: moment().add(11, 'm').format('yyyyMMDDHHmmss')
    };
    payData.sign = security.generateOrderSign(payData);
    let xmlData = xmlBuilder.buildObject(payData);
    let response = await axios.post(wxConfig.apis.unionOrderUrl, xmlData);
    let returnData = await xmlParser.parseStringPromise(response.data);
    if (returnData.xml.return_code === 'SUCCESS' && returnData.xml.result_code === 'SUCCESS') {
        return returnData.xml.prepay_id;
    } else {
        throw new WxApiError.RequestPrepayError('订单创建失败');
    }
}

/**
 * 统一退款接口
 * @param refundId      退款id
 * @param orderId       订单id
 * @param price         退款金额
 * @param refundDesc    退款描述
 * @param refundNotifyUrl   退款回调地址
 * @returns refundId
 */
async function orderRefund (refundId, orderId, price, refundDesc, refundNotifyUrl) {
    let [xmlBuilder, xmlParser] = getXmlTool();
    let refundData = {
        appid: wxConfig.secret.appId,
        mch_id: wxConfig.payment.mchId,
        nonce_str: generateRandomUUID(),
        sign_type: 'HMAC-SHA256',
        out_trade_no: orderId,
        out_refund_no: refundId,
        total_fee: price,
        refund_fee: price,
        refund_desc: refundDesc,
        notify_url: refundNotifyUrl
    };
    refundData.sign = security.generateOrderSign(refundData);
    let xmlData = xmlBuilder.buildObject(refundData);
    let httpAgent;
    try {
        httpAgent = new https.Agent({
            cert: fs.readFileSync(path.resolve(__dirname, '../../auth/apiclient_cert.pem')),
            key: fs.readFileSync(path.resolve(__dirname, '../../auth/apiclient_key.pem'))
        });
    } catch (e) {
        throw new WxApiError.RequestRefundError('证书读取失败');
    }
    let response = await axios.post(wxConfig.apis.refundUrl, xmlData, { httpsAgent: httpAgent });
    let returnData = await xmlParser.parseStringPromise(response.data);
    if (returnData.xml.return_code === 'SUCCESS' && returnData.xml.result_code === 'SUCCESS') {
        return refundId;
    } else {
        throw new WxApiError.RequestRefundError('退款失败');
    }
}

/**
 * 企业付款到零钱
 * @param transferOrderId   订单号
 * @param openid            用户openid
 * @param amount            金额
 * @param description       提现描述
 * @returns payment_no      交易号
 */
async function transferMoneyToUser (transferOrderId, openid, amount, description) {
    let [xmlBuilder, xmlParser] = getXmlTool();
    let queryData = {
        mch_appid: wxConfig.secret.appId,
        mchid: wxConfig.payment.mchId,
        partner_trade_no: transferOrderId,
        openid: openid,
        check_name: 'NO_CHECK',
        amount: amount,
        desc: description,
        nonce_str: generateRandomUUID()
    };
    queryData.sign = security.generateOrderSign(queryData, true);
    let xmlData = xmlBuilder.buildObject(queryData);
    let httpAgent;
    try {
        httpAgent = new https.Agent({
            cert: fs.readFileSync(path.resolve(__dirname, '../../auth/pt_cert.pem')),
            key: fs.readFileSync(path.resolve(__dirname, '../../auth/pt_key.pem'))
        });
    } catch (e) {
        throw new WxApiError.RequestTransferError('证书读取失败');
    }
    let response = await axios.post(wxConfig.apis.payToUserUrl, xmlData, { httpsAgent: httpAgent });
    let returnData = await xmlParser.parseStringPromise(response.data);
    if (returnData.xml.return_code === 'SUCCESS' && returnData.xml.result_code === 'SUCCESS') {
        return returnData.xml.payment_no;
    } else {
        throw new WxApiError.RequestTransferError(returnData.xml.err_code_des);
    }
}

/**
 * 确认订单是否已支付
 * @param orderId       订单id
 * @returns boolean
 */
async function orderHasPayed (orderId) {
    let [xmlBuilder, xmlParser] = getXmlTool();
    let queryData = {
        appid: wxConfig.secret.appId,
        mch_id: wxConfig.payment.mchId,
        nonce_str: generateRandomUUID(),
        sign_type: 'HMAC-SHA256',
        out_trade_no: orderId
    };
    queryData.sign = security.generateOrderSign(queryData);
    let xmlData = xmlBuilder.buildObject(queryData);
    let response = await axios.post(wxConfig.apis.orderQueryUrl, xmlData);
    let returnData = await xmlParser.parseStringPromise(response.data);
    if (returnData.xml.return_code === 'SUCCESS' && returnData.xml.result_code === 'SUCCESS') {
        return returnData.xml.trade_state === 'SUCCESS';
    }
    return false;
}

module.exports = {
    getXmlTool,
    getPaymentNotifyHost,
    makeMehBill,
    orderRefund,
    transferMoneyToUser,
    orderHasPayed
};
